package hikari.gui.panels.particle;

import com.gooddies.events.ValueChangedEvent;
import com.gooddies.swing.RowLayout;
import com.gooddies.swing.VerticalLayout;
import com.gooddies.swing.hComboBox;
import com.gooddies.texteditors.DefaultFloatTextField;
import com.gooddies.texteditors.DefaultIntegerTextField;
import com.gooddies.wiring.Wiring;
import com.gooddies.wiring.annotations.PostWiring;
import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.gui.guicomponents.ColorPicker;
import hikari.gui.guicomponents.ExpandablePanel;
import hikari.gui.guicomponents.hLabel;
import hikari.hcomponents.HComponent;
import hikari.hcomponents.RenderEvent;
import hikari.managers.Manager3D;
import hikari.objects3d.Particle3dObject;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.swig.jirr.IParticleBoxEmitter;
import org.swig.jirr.IParticleCylinderEmitter;
import org.swig.jirr.IParticleEmitter;
import org.swig.jirr.IParticlePointEmitter;
import org.swig.jirr.IParticleRingEmitter;
import org.swig.jirr.IParticleSphereEmitter;
import org.swig.jirr.IParticleSystemSceneNode;
import org.swig.jirr.IVideoDriver;
import org.swig.jirr.SColor;
import org.swig.jirr.dimension2df;
import org.swig.jirr.vector3df;

/**
 * @author Dmitry
 */
@WiringComponent(singleton = true, lazy = true)
@SuppressWarnings("unchecked")
public class Particle3dPanel extends HComponent {

    private enum EmitterType {

        Box, Cylinder, Point, Ring, Sphere
    }
    private hComboBox<EmitterType> emitterTypeCombo;
    private Particle3dObject particleSystem;
    private DefaultFloatTextField speedEditor;
    private DefaultIntegerTextField maxAngleDegr;
    private DefaultIntegerTextField maxLifeTime;
    private DefaultIntegerTextField maxPartPerSec;
    private ColorPicker maxStartColor;
    private Dimension2dEditor maxStartSize;
    private DefaultIntegerTextField minLifeTime;
    private DefaultIntegerTextField minPartPerSec;
    private ColorPicker minStartColor;
    private Dimension2dEditor minStartSize;
    private EmitterEditorPanel emitterEditorPanel;
    @Wire
    private Manager3D manager3D;

    private Particle3dPanel() {
        int x=0;
        x++;
    }

    @PostWiring
    private void init() {
        int width = 40;
        setLayout(new VerticalLayout() {
            @Override
            public Dimension preferredLayoutSize(Container parent) {
                return super.preferredLayoutSize(parent);
            }
        }.setStartGap(20).setEndGap(10).setSideGap(5));
        setTitle("Particle System");
        ExpandablePanel basicPParam = Wiring.getComponent(ExpandablePanel.class);
        basicPParam.setLayout(new RowLayout().setStartYGap(20).setEndYGap(10).setStartXGap(10).setEndXGap(10));
        add(basicPParam);
        basicPParam.setTitle("Particle System Params");

        basicPParam.add(new hLabel("Speed"));
        speedEditor = new DefaultFloatTextField();
        speedEditor.setPreferredSize(new Dimension(100, 20));
        speedEditor.setValueChangedEventNumber(new ValueChangedEvent<Float>() {
            @Override
            public void valueChanged(Float value) {
                speedChange();
            }
        });

        basicPParam.add(speedEditor, RowLayout.NewRow);
        maxAngleDegr = new DefaultIntegerTextField();
        maxAngleDegr.setPreferredSize(new Dimension(width, 20));
        maxAngleDegr.setValueChanged(new ValueChangedEvent<Integer>() {
            @Override
            public void valueChanged(Integer value) {
                maxAngleDegrChange();
            }
        });

        basicPParam.add(new hLabel("Max Angle Degrees"));
        basicPParam.add(maxAngleDegr, RowLayout.NewRow);
        maxLifeTime = new DefaultIntegerTextField();
        maxLifeTime.setPreferredSize(new Dimension(width, 20));
        maxLifeTime.setValueChanged(new ValueChangedEvent<Integer>() {
            @Override
            public void valueChanged(Integer value) {
                maxLifeTimeChange();
            }
        });

        basicPParam.add(new hLabel("Max Life Time"));
        basicPParam.add(maxLifeTime, RowLayout.NewRow);
        maxPartPerSec = new DefaultIntegerTextField();
        maxPartPerSec.setPreferredSize(new Dimension(width, 20));
        maxPartPerSec.setValueChanged(new ValueChangedEvent<Integer>() {
            @Override
            public void valueChanged(Integer value) {
                maxParticlesPerSecondChange();
            }
        });

        basicPParam.add(new hLabel("Max Particles Per Second"));
        basicPParam.add(maxPartPerSec, RowLayout.NewRow);
        maxStartColor = new ColorPicker();
        maxStartColor.setValueChangedEvent(new ValueChangedEvent<Color>() {
            @Override
            protected void valueChanged(Color value) {
                maxColorChange(value);

            }
        });

        basicPParam.add(new hLabel("Max Start Color"));
        basicPParam.add(maxStartColor, RowLayout.NewRow);

        basicPParam.add(new hLabel("Max Size:"));
        maxStartSize = new Dimension2dEditor(basicPParam);
        maxStartSize.setValueChangedEvent(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                maxStartSizeChange();
            }
        });

        minLifeTime = new DefaultIntegerTextField();
        minLifeTime.setPreferredSize(new Dimension(width, 20));
        minLifeTime.setValueChanged(new ValueChangedEvent<Integer>() {
            @Override
            public void valueChanged(Integer value) {
                minLifeTimeChange();
            }
        });

        basicPParam.add(new hLabel("Min Life Time"));
        basicPParam.add(minLifeTime, RowLayout.NewRow);
        minPartPerSec = new DefaultIntegerTextField();
        minPartPerSec.setPreferredSize(new Dimension(width, 20));
        minPartPerSec.setValueChanged(new ValueChangedEvent<Integer>() {
            @Override
            public void valueChanged(Integer value) {
                minParticlesPerSecondChange();
            }
        });

        basicPParam.add(new hLabel("Min Particles Per Second"));
        basicPParam.add(minPartPerSec, RowLayout.NewRow);
        minStartColor = new ColorPicker();
        minStartColor.setValueChangedEvent(new ValueChangedEvent<Color>() {
            @Override
            protected void valueChanged(Color value) {
                minColorChange(value);
            }
        });

        basicPParam.add(new hLabel("Min Start Color"));
        basicPParam.add(minStartColor, RowLayout.NewRow);

        basicPParam.add(new hLabel("Min Size:"));
        minStartSize = new Dimension2dEditor(basicPParam);
        minStartSize.setValueChangedEvent(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                minStartSizeChange();
            }
        });
        basicPParam.add(new hLabel("Emitter:"));
        emitterTypeCombo = new hComboBox<EmitterType>();
        for (EmitterType type : EmitterType.values()) {
            emitterTypeCombo.addItem(type);
        }
        emitterTypeCombo.setValueChangedEvent(new ValueChangedEvent<EmitterType>() {
            @Override
            protected void valueChanged(EmitterType value) {
                changeEmitter();
            }
        });

        basicPParam.add(emitterTypeCombo, RowLayout.NewRow);
    }

    private void setNewEmitter(IParticleSystemSceneNode ps, EmitterType type) {
        switch (type) {
            case Box: {
                IParticleBoxEmitter emitter = ps.createBoxEmitter();
                ps.setEmitter(emitter);
                break;
            }
            case Cylinder: {
                IParticleEmitter emitter = ps.createCylinderEmitter(vector3df.NULLVECTOR, 5, ps.getRotation().rotationToDirection(vector3df.DIR_UP), 5);
                ps.setEmitter(emitter);
                break;
            }

            case Point: {
                IParticleEmitter emitter = ps.createPointEmitter();
                ps.setEmitter(emitter);
                break;
            }

            case Ring: {
                IParticleEmitter emitter = ps.createRingEmitter(vector3df.NULLVECTOR, 5, 3);
                ps.setEmitter(emitter);
                break;
            }

            case Sphere: {
                IParticleEmitter emitter = ps.createSphereEmitter(vector3df.NULLVECTOR, 5);
                ps.setEmitter(emitter);
                break;
            }
        }
    }

    private void changeEmitter() {
        if (emitterEditorPanel != null) {
            emitterEditorPanel.getParent().remove(emitterEditorPanel);
            emitterEditorPanel = null;
        }

        EmitterType emitterType = emitterTypeCombo.getSelectedItem();
        setNewEmitter(particleSystem.getParticleSystem(), emitterType);

        createEmitterPanel(particleSystem);
        speedChange();
        maxAngleDegrChange();
        maxLifeTimeChange();
        minLifeTimeChange();
        maxParticlesPerSecondChange();
        minParticlesPerSecondChange();
        maxColorChange(maxStartColor.getSelectedColor());
        minColorChange(minStartColor.getSelectedColor());
        minStartSizeChange();
        maxStartSizeChange();
        setPreferredSize(null);
        validate();
        repaint();
    }

    private void speedChange() {
        float speed = (float) speedEditor.getValue();
        IParticleEmitter emitter = particleSystem.getParticleSystem().getEmitter();
        emitter.setDirection(emitter.getDirection().setLength(speed));
    }

    private void maxAngleDegrChange() {
        int maxAngleDegrees = this.maxAngleDegr.getValue();
        particleSystem.getParticleSystem().getEmitter().setMaxAngleDegrees(maxAngleDegrees);
    }

    private void maxLifeTimeChange() {
        int tMaxLifeTime = maxLifeTime.getValue();
        particleSystem.getParticleSystem().getEmitter().setMaxLifeTime(tMaxLifeTime);
    }

    private void minLifeTimeChange() {
        int tMinLifeTime = minLifeTime.getValue();
        particleSystem.getParticleSystem().getEmitter().setMinLifeTime(tMinLifeTime);
    }

    private void maxParticlesPerSecondChange() {
        int tMaxParticlesPerSecond = maxPartPerSec.getValue();
        particleSystem.getParticleSystem().getEmitter().setMaxParticlesPerSecond(tMaxParticlesPerSecond);
    }

    private void minParticlesPerSecondChange() {
        int tMinParticlesPerSecond = minPartPerSec.getValue();
        particleSystem.getParticleSystem().getEmitter().setMinParticlesPerSecond(tMinParticlesPerSecond);
    }

    private void maxColorChange(Color col) {
        SColor color = new SColor(col);
        particleSystem.getParticleSystem().getEmitter().setMaxStartColor(color);
    }

    private void minColorChange(Color col) {
        SColor color = new SColor(col);
        particleSystem.getParticleSystem().getEmitter().setMinStartColor(color);
    }

    private void maxStartSizeChange() {
        dimension2df dimension = maxStartSize.getData();
        particleSystem.getParticleSystem().getEmitter().setMaxStartSize(dimension);
    }

    private void minStartSizeChange() {
        dimension2df dimension = minStartSize.getData();
        particleSystem.getParticleSystem().getEmitter().setMinStartSize(dimension);
    }

    private EmitterEditorPanel getEmitterEditorPanel(Particle3dObject particleSystem) {
        EmitterEditorPanel editorPanel = null;
        IParticleEmitter emitter = particleSystem.getParticleSystem().getEmitter();
        EmitterType type = getEmitterType(emitter);
        if (type == EmitterType.Box) {
            editorPanel = new BoxEmitterEditorPanel();
        } else if (type == EmitterType.Sphere) {
            editorPanel = new SphereEmitterEditorPanel();
        } else if (type == EmitterType.Ring) {
            editorPanel = new RingEmitterEditorPanel();
        } else if (type == EmitterType.Point) {
            editorPanel = null;
        } else if (type == EmitterType.Cylinder) {
            editorPanel = new CylinderEmitterEditorPanel();
        } else {
            throw new RuntimeException("Cannot create editor panel for particle type " + emitter.getClass().getCanonicalName());
        }

        return editorPanel;
    }

    private EmitterType getEmitterType(IParticleEmitter emitter) {
        if (emitter instanceof IParticleBoxEmitter) {
            return EmitterType.Box;
        } else if (emitter instanceof IParticleSphereEmitter) {
            return EmitterType.Sphere;
        } else if (emitter instanceof IParticleRingEmitter) {
            return EmitterType.Ring;
        } else if (emitter instanceof IParticlePointEmitter) {
            return EmitterType.Point;
        } else if (emitter instanceof IParticleCylinderEmitter) {
            return EmitterType.Cylinder;
        } else {
            throw new RuntimeException("Cannot get particle type for emitter " + emitter.getClass().getCanonicalName());
        }
    }

    private void createEmitterPanel(Particle3dObject particleSystem) {
        EmitterEditorPanel editorPanel = getEmitterEditorPanel(particleSystem);
        if (editorPanel != null) {
            emitterEditorPanel = editorPanel;
            add(emitterEditorPanel);
            emitterEditorPanel.putFromEmmiter(particleSystem.getParticleSystem().getEmitter());
        }
    }

    public Particle3dPanel updateWith3dObject(Particle3dObject particleSystem) {
        if (emitterEditorPanel != null) {
            emitterEditorPanel.getParent().remove(emitterEditorPanel);
        }
        this.particleSystem = particleSystem;
        IParticleEmitter emitter = particleSystem.getParticleSystem().getEmitter();
        speedEditor.setValue(emitter.getDirection().getLength());
        maxAngleDegr.setValue(emitter.getMaxAngleDegrees());
        maxLifeTime.setValue(emitter.getMaxLifeTime());
        maxPartPerSec.setValue(emitter.getMaxParticlesPerSecond());
        maxStartColor.setSelectedColor(emitter.getMaxStartColor());
        maxStartSize.setData(emitter.getMaxStartSize());
        minLifeTime.setValue(emitter.getMinLifeTime());
        minPartPerSec.setValue(emitter.getMinParticlesPerSecond());
        minStartColor.setSelectedColor(emitter.getMinStartColor());
        minStartSize.setData(emitter.getMinStartSize());
        EmitterType type = getEmitterType(emitter);
        emitterTypeCombo.setSelectedItem(type);
        createEmitterPanel(particleSystem);
        setPreferredSize(null);
        validate();
        repaint();
        return this;
    }

    @Override
    public RenderEvent registerAfterRenderBeforeGuiEvent() {
        return new RenderEvent() {
            int counter = 0;

            @Override
            public void onRender() {
                if (emitterEditorPanel != null) {
                    IVideoDriver driver = manager3D.getVideoDriver();
                    driver.setTransformationAsInNode(particleSystem.getParticleSystem());
                    emitterEditorPanel.renderEmitter(driver);
                }

                counter++;
                if (particleSystem != null) {
                    IParticleEmitter emitter = particleSystem.getParticleSystem().getEmitter();
                    if (counter % 50 == 0) {
                        vector3df rot = particleSystem.getParticleSystem().getRotation();
                        vector3df speed = rot.rotationToDirection(new vector3df(0, (float) speedEditor.getValue(), 0));
                        emitter.setDirection(speed);
                    }
                }
            }
        };
    }
}
